package cn.hn.monkey.stock.data;

import static cn.hn.monkey.stock.tools.WhatHandler.REFRESH_HANDLE_TIPS;
import static cn.hn.monkey.stock.tools.WhatHandler.REFRESH_LOOP;
import static cn.hn.monkey.stock.tools.WhatHandler.REFRESH_STOCK;
import static cn.hn.monkey.stock.tools.WhatHandler.REFRESH_SH_TIPS;
import static cn.hn.monkey.stock.tools.WhatHandler.REFRESH_SZ_TIPS;
import static cn.hn.monkey.stock.tools.WhatHandler.REFRESH_WAIT;

import android.os.Environment;
import android.os.Handler;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import cn.hn.monkey.stock.data.SaveData.StockDetail;

public class TodayData {

    public static TodayData getInstance() {
        if (instance == null) {
            instance = new TodayData();
        }
        return instance;
    }

    private static TodayData instance = null;
    final String TAG = "TodayData";
    final int REFRESH_CLOSE_TIME = 600;
    final int REFRESH_OPEN_TIME = 60;
    final int REFRESH_SLEEP_TIME = 1800;

    private TodayData() {
        refresh = new RefreshTodayPrice();
        refresh.loadStockInformation();
    }

    RefreshTodayPrice refresh;

    public void destroy() {
        if(refresh != null) refresh.stop();
    }

    public void startFresh(Handler handler) {
        refresh.handler = handler;
        new Thread(refresh).start();
    }

    public void stop() {
        refresh.stop();
    }

    class RefreshTodayPrice implements Runnable {

        // http://web.juhe.cn:8080/finance/stock/szall?page=0&type=0&key=30d4cbdb414ad3c4dbdca03a5347f7f2

        final String urlFormatStr = "http://web.juhe.cn:8080/finance/stock/s%call?page=%d&type=%d&key=";
        final String keyFromJuhe = "30d4cbdb414ad3c4dbdca03a5347f7f2";
        volatile boolean working = true;
        int slot = 0;  // 时段：0 = 周六周日，00:00~06:00，18:00~23:59；1 = 09:00~15:30；2 = other
        int show = 0;  // 显示的序号，每轮加一
        int loop = 0;  // 循环显示序号

        Handler handler;

        public void stop() {
            working = false;
            Log.e(TAG, "Refreah stop, working:" + working);
        }

        @Override
        public void run() {

            int timeCnt = 10;
            int lastDay = 0;

            Log.e(TAG, "Refresh start!");

            first = true;
            while(working) {

                Calendar c = Calendar.getInstance();
                int today = c.get(Calendar.YEAR) * 10000 + (c.get(Calendar.MONTH) + 1) * 100 + c.get(Calendar.DAY_OF_MONTH);
                if(today != lastDay) {
                    lastDay = today;
                }
                int week = c.get(Calendar.DAY_OF_WEEK);
                int time = c.get(Calendar.HOUR_OF_DAY) * 100 + c.get(Calendar.MINUTE);
                if(week == 1 || week == 7 || time < 600 || time > 1800) {
                    slot = 0;
                    timeCnt = REFRESH_SLEEP_TIME;
                } else if(time > 900 && time < 1530) {
                    slot = 1;
                    timeCnt = REFRESH_OPEN_TIME;
                } else {
                    slot = 2;
                    timeCnt = REFRESH_CLOSE_TIME;
                }

                fresh();
                if(first) first = false;

                try {
                    while(--timeCnt > 0 && working) {
                        for(int n = 0; n < 10; n++) {
                            Thread.sleep(100);
                            if(n == 1 || n == 4 || n == 7) {
                                handler.obtainMessage(REFRESH_LOOP, loop+1, listStockDetails.size(), listStockDetails.get(loop)).sendToTarget();
                                if(++loop >= listStockDetails.size()) loop = 0;
                            }
                        }
                        Log.e(TAG, "wait:" + timeCnt);
                        handler.obtainMessage(REFRESH_WAIT, timeCnt, 0).sendToTarget();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Log.e(TAG, "Refresh destroy!");
        }

        volatile boolean first;
        volatile int nextpage;
        volatile int timeout;
        volatile int error;
        volatile int total;
        volatile int agtotal;
        volatile int lineperpage;
        volatile boolean intact;

        void fresh() {
            final int modeLinePerPage = 4;  // 4 = 80
            lineperpage = 80;
            agtotal = 0;
            intact = true;
            Log.e(TAG, "fresh begin");
            for(int i=0; i<2 && working; i++) {
                handler.sendEmptyMessage(i==0?REFRESH_SH_TIPS:REFRESH_SZ_TIPS);
                error = 0;
                nextpage = 1;
                while(nextpage > 0 && working) {
                    String url = String.format(urlFormatStr, i == 0 ? 'h' : 'z', nextpage, modeLinePerPage);
                    Log.e(TAG, url);

                    OkHttpClient okHttpClient = new OkHttpClient();
                    Request request = new Request.Builder().url(url + keyFromJuhe).build();
                    okHttpClient.newCall(request).enqueue(callback);

                    timeout = 120;
                    while(timeout > 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        timeout --;
                    }

                    if(timeout == 0) {
                        // 自然递减到此，表示上一次的访问尚未完成
                        i = 2;
                        intact = false;
                        // 停止本轮刷新
                        break;
                    }

                }
            }
            if(working) {
                handler.sendEmptyMessage(REFRESH_HANDLE_TIPS);
                Log.e(TAG, "fresh end, agtotal:" + agtotal);
                ranking.update();
                Log.e(TAG, "ranking finish, intact:" + intact);
                if (intact) {
                    saveStockInformation();
                    Log.e(TAG, "save stock information finish");
                }
            }
        }

        Callback callback = new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "Refresh failure, timeout:" + timeout);
                timeout = 0;
                if(++ error >= 3) nextpage = 0;
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e(TAG, "Refresh success, timeout:" + timeout);
                error = 0;
                final String resp = response.body().string();
                Gson gson = new Gson();
                RefreshResult rr = gson.fromJson(resp, new TypeToken<RefreshResult>() {}.getType());
                if(rr != null) {
                    if(rr.error_code == 0) {
                        total = Integer.parseInt(rr.result.totalCount);
                        int agcnt = 0;
                        int index = 0;
                        int delta = 0;
                        StockDetail sd;
                        for(ResultDetail rd : rr.result.data) {
                            if(UtilsData.isAG(rd.symbol)) {
                                sd = InsertStock(rd, first);
                                if(sd == null) continue;
                                sd.trade = Float.parseFloat(rd.trade);
                                sd.pricechange = Float.parseFloat(rd.pricechange);
                                sd.changepercent = Float.parseFloat(rd.changepercent);
                                sd.buy = Float.parseFloat(rd.buy);
                                sd.sell = Float.parseFloat(rd.sell);
                                sd.settlement = Float.parseFloat(rd.settlement);
                                sd.open = Float.parseFloat(rd.open);
                                sd.high = Float.parseFloat(rd.high);
                                sd.low = Float.parseFloat(rd.low);
                                float vol = Float.parseFloat(rd.volume);
                                delta = (int)(vol - sd.volume);
                                if(delta < 0) delta = 0;
                                sd.volume = vol - sd.volume;
                                sd.amount = Float.parseFloat(rd.amount);
                                sd.zhenfu = sd.low > 0 ? (sd.high - sd.low) / sd.low : 0;
                                sd.ticktime = rd.ticktime;
                                agtotal ++;
                                agcnt ++;
                                if(index == (show % lineperpage)) handler.obtainMessage(REFRESH_STOCK, delta,0, sd).sendToTarget();
                                index ++;
                            }
                        }
                        show ++;
                        Log.e(TAG, "pos:" + (nextpage * lineperpage) + ", cnt:" + total + ", size:" + rr.result.data.size() + ", ag:" + agcnt);
                        if(nextpage * lineperpage < total) {
                            timeout = 0;
                            nextpage ++;
                            return;
                        }
                        Log.e(TAG, "end");
                    }
                }
                timeout = 0;
                nextpage = 0;
            }
        };

        class RefreshResult {
            int error_code;
            String reason;
            ResultSurvey result;
        }

        class ResultSurvey {
            String totalCount;
            String page;
            String num;
            List<ResultDetail> data;
        }

        class ResultDetail {
            String symbol;
            String name;
            String trade;
            String pricechange;
            String changepercent;
            String buy;
            String sell;
            String settlement;
            String open;
            String high;
            String low;
            String volume;
            String amount;
            String code;
            String ticktime;
        }

        List<StockDetail> listStockDetails = new ArrayList<StockDetail>();

        StockDetail InsertStock(ResultDetail rd, boolean first) {
            for(StockDetail sd : listStockDetails) {
                if(sd.symbol.equals(rd.symbol)) return sd;
            }
            if(first) {
                StockDetail sdTmp = new StockDetail();
                sdTmp.symbol = rd.symbol;
                sdTmp.name = rd.name;
                sdTmp.code = rd.code;
                int i=0;
                for(StockDetail sd : listStockDetails) {
                    if(sd.symbol.compareTo(rd.symbol) > 0) {
                        listStockDetails.add(i, sdTmp);
                        break;
                    }
                    i ++;
                }
                if(i == listStockDetails.size()) {
                    listStockDetails.add(sdTmp);
                }
                return sdTmp;
            }
            return null;
        }

        class Ranking {
            int[] zhenfu;
            int[] amount;
            int[] changepercent;

            void update() {
                int size = listStockDetails.size();
                if(zhenfu == null || zhenfu.length != size) zhenfu = new int[size];
                if(amount == null || amount.length != size) amount = new int[size];
                if(changepercent == null || zhenfu.length != size) changepercent = new int[size];
                int i, j, k;
                for(i=0; i<size; i++) {
                    zhenfu[i] = i;
                    amount[i] = i;
                    changepercent[i] = i;
                }
                StockDetail sd1, sd2;
                for(i=0; i<size; i++) {
                    for(j=i+1; j<size; j++) {
                        sd1 = listStockDetails.get(zhenfu[i]);
                        sd2 = listStockDetails.get(zhenfu[j]);
                        if(sd1.zhenfu < sd2.zhenfu) {
                            k = zhenfu[i];
                            zhenfu[i] = zhenfu[j];
                            zhenfu[j] = k;
                        }
                        sd1 = listStockDetails.get(amount[i]);
                        sd2 = listStockDetails.get(amount[j]);
                        if(sd1.amount < sd2.amount) {
                            k = amount[i];
                            amount[i] = amount[j];
                            amount[j] = k;
                        }
                        sd1 = listStockDetails.get(changepercent[i]);
                        sd2 = listStockDetails.get(changepercent[j]);
                        if(sd1.changepercent < sd2.changepercent) {
                            k = changepercent[i];
                            changepercent[i] = changepercent[j];
                            changepercent[j] = k;
                        }
                    }
                }
            }
        }

        Ranking ranking = new Ranking();

        String getTodayStockInformationFileName() {
            Calendar c = Calendar.getInstance();
            String fileName = "cn" + (c.get(Calendar.YEAR) * 10000 + (c.get(Calendar.MONTH) + 1) * 100 + c.get(Calendar.DAY_OF_MONTH)) + ".day";
            return fileName;
        }

        void loadStockInformation() {
            listStockDetails.clear();
            /*
            File todayFile = new File(Environment.getExternalStorageDirectory(), "Clockwise/" + getTodayStockInformationFileName());
            if(todayFile.exists()) {
                FileInputStream fis;
                ObjectInputStream ois;
                try {
                    fis = new FileInputStream(todayFile);
                    ois = new ObjectInputStream(fis);
                    StockDetail[] obj = (StockDetail[]) ois.readObject();
                    List<StockDetail> temp = Arrays.asList(obj);
                    listStockDetails = new ArrayList<StockDetail>(temp);
                    fis.close();
                    Log.e(TAG, "loadStockInformation, size:" + listStockDetails.size());
                } catch (Exception e) {
                    Log.e(TAG, "loadStockInformation, fails");
                    e.printStackTrace();
                }
            }
             */
        }

        void saveStockInformation() {
            FileOutputStream fos;
            File todayFile = new File(Environment.getExternalStorageDirectory(), "Clockwise/" + getTodayStockInformationFileName());
            try {
                fos = new FileOutputStream(todayFile);
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                StockDetail[] obj = new StockDetail[listStockDetails.size()];
                listStockDetails.toArray(obj);
                oos.writeObject(obj);
                oos.close();
                fos.close();
                Log.e(TAG, "saveStockInformation, size:" + listStockDetails.size());
            } catch (Exception e) {
                Log.e(TAG, "saveStockInformation, fails");
                e.printStackTrace();
            }
        }

    }
}
